#ifndef TB_REFLECTION_PARAM_HOLDER_HPP__
#define TB_REFLECTION_PARAM_HOLDER_HPP__

#include <stack>

#include "variant.hpp"
#include "reflection_fwd.hpp"

TB_NAMESPACE_BEGIN

struct ParamHolder {

  virtual uint32 GetParamCount() const = 0;

  virtual Variant *GetParams() const = 0;
};

struct StackParamHolder {

  virtual uint32 GetParamCount() const = 0;

  virtual const std::stack<Variant> &GetParams() const = 0;
};

typedef shared_ptr<StackParamHolder> StackParamHolderPtr;

static Variant* MakeParamStorage() {
  return nullptr;
}

template <typename Param1Type>
Variant* MakeParamStorage(Param1Type p1) {
  Variant *params_ = new Variant[1];
  new (params_) Variant::FromValue(p1);
  return params_;
}

template <typename Param1Type, typename Param2Type>
Variant* MakeParamStorage(Param1Type p1, Param2Type p2) {
  Variant *params_ = new Variant[2];
  new (params_) Variant::FromValue(p1);
  new (params_ + 1) Variant::FromValue(p2);
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type>
Variant* MakeParamStorage(Param1Type p1, Param2Type p2, Param3Type p3) {
  Variant *params_ = new Variant[3];
  new (params_) Variant::FromValue(p1);
  new (params_ + 1) Variant::FromValue(p2);
  new (params_ + 2) Variant::FromValue(p3);
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type, typename Param4Type>
Variant* MakeParamStorage(Param1Type p1, Param2Type p2, Param3Type p3, Param4Type p4) {
  Variant *params_ = new Variant[4];
  new (params_) Variant::FromValue(p1);
  new (params_ + 1) Variant::FromValue(p2);
  new (params_ + 2) Variant::FromValue(p3);
  new (params_ + 3) Variant::FromValue(p4);
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type, typename Param4Type, typename Param5Type>
Variant* MakeParamStorage(Param1Type p1, Param2Type p2, Param3Type p3, Param4Type p4, Param5Type p5) {
  Variant *params_ = new Variant[5];
  new (params_) Variant::FromValue(p1);
  new (params_ + 1) Variant::FromValue(p2);
  new (params_ + 2) Variant::FromValue(p3);
  new (params_ + 3) Variant::FromValue(p4);
  new (params_ + 4) Variant::FromValue(p5);
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type, typename Param4Type, typename Param5Type, typename Param6Type>
Variant* MakeParamStorage(Param1Type p1, Param2Type p2, Param3Type p3, Param4Type p4, Param5Type p5, Param6Type p6) {
  Variant *params_ = new Variant[6];
  new (params_) Variant::FromValue(p1);
  new (params_ + 1) Variant::FromValue(p2);
  new (params_ + 2) Variant::FromValue(p3);
  new (params_ + 3) Variant::FromValue(p4);
  new (params_ + 4) Variant::FromValue(p5);
  new (params_ + 5) Variant::FromValue(p6);
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type, typename Param4Type, typename Param5Type, typename Param6Type, typename Param7Type>
Variant* MakeParamStorage(Param1Type p1, Param2Type p2, Param3Type p3, Param4Type p4, Param5Type p5, Param6Type p6, Param7Type p7) {
  Variant *params_ = new Variant[7];
  new (params_) Variant::FromValue(p1);
  new (params_ + 1) Variant::FromValue(p2);
  new (params_ + 2) Variant::FromValue(p3);
  new (params_ + 3) Variant::FromValue(p4);
  new (params_ + 4) Variant::FromValue(p5);
  new (params_ + 5) Variant::FromValue(p6);
  new (params_ + 6) Variant::FromValue(p7);
  return param_;
}

template <typename... VarsType>
struct VariadicParamHolder : public ParamHolder {
  VariadicParamHolder(VarsType... params) {
    params_ = MakeParamStorage(params...);
  }
  ~VariadicParamHolder() {
    delete[] params_;
  }
  uint32 GetParamCount() const OVERRITE {
    return boost::mpl::size<boost::mpl::vector<VarsType...> >::value;
  }
  Variant *GetParams() const OVERRITE {
    return params_;
  }
private:
  Variant *params_;
};

static std::stack<Variant>* MakeStackParamStorage() {
  return new std::stack<Variant>();
}

template <typename Param1Type>
std::stack<Variant>* MakeStackParamStorage(Param1Type p1) {
  std::stack<Variant>* params_ = new std::stack<Variant>();
  params_->push(Variant::FromValue(p1));
  return params_;
}

template <typename Param1Type, typename Param2Type>
std::stack<Variant>* MakeStackParamStorage(Param1Type p1, Param2Type p2) {
  std::stack<Variant>* params_ = new std::stack<Variant>();
  params_->push(Variant::FromValue(p2));
  params_->push(Variant::FromValue(p1));
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type>
std::stack<Variant>* MakeStackParamStorage(Param1Type p1, Param2Type p2, Param3Type p3) {
  std::stack<Variant>* params_ = new std::stack<Variant>();
  params_->push(Variant::FromValue(p3));
  params_->push(Variant::FromValue(p2));
  params_->push(Variant::FromValue(p1));
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type, typename Param4Type>
std::stack<Variant>* MakeStackParamStorage(Param1Type p1, Param2Type p2, Param3Type p3, Param4Type p4) {
  std::stack<Variant>* params_ = new std::stack<Variant>();
  params_->push(Variant::FromValue(p4));
  params_->push(Variant::FromValue(p3));
  params_->push(Variant::FromValue(p2));
  params_->push(Variant::FromValue(p1));
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type, typename Param4Type, typename Param5Type>
std::stack<Variant>* MakeStackParamStorage(Param1Type p1, Param2Type p2, Param3Type p3, Param4Type p4, Param5Type p5) {
  std::stack<Variant>* params_ = new std::stack<Variant>();
  params_->push(Variant::FromValue(p5));
  params_->push(Variant::FromValue(p4));
  params_->push(Variant::FromValue(p3));
  params_->push(Variant::FromValue(p2));
  params_->push(Variant::FromValue(p1));
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type, typename Param4Type, typename Param5Type, typename Param6Type>
std::stack<Variant>* MakeStackParamStorage(Param1Type p1, Param2Type p2, Param3Type p3, Param4Type p4, Param5Type p5, Param6Type p6) {
  std::stack<Variant>* params_ = new std::stack<Variant>();
  params_->push(Variant::FromValue(p6));
  params_->push(Variant::FromValue(p5));
  params_->push(Variant::FromValue(p4));
  params_->push(Variant::FromValue(p3));
  params_->push(Variant::FromValue(p2));
  params_->push(Variant::FromValue(p1));
  return params_;
}

template <typename Param1Type, typename Param2Type, typename Param3Type, typename Param4Type, typename Param5Type, typename Param6Type, typename Param7Type>
std::stack<Variant>* MakeStackParamStorage(Param1Type p1, Param2Type p2, Param3Type p3, Param4Type p4, Param5Type p5, Param6Type p6, Param7Type p7) {
  std::stack<Variant>* params_ = new std::stack<Variant>();
  params_->push(Variant::FromValue(p7));
  params_->push(Variant::FromValue(p6));
  params_->push(Variant::FromValue(p5));
  params_->push(Variant::FromValue(p4));
  params_->push(Variant::FromValue(p3));
  params_->push(Variant::FromValue(p2));
  params_->push(Variant::FromValue(p1));
  return param_;
}

template <typename... VarsType>
struct VariadicStackParamHolder : public StackParamHolder {
  VariadicStackParamHolder(VarsType... params) {
    params_ = MakeStackParamStorage(params...);
  }
  ~VariadicStackParamHolder() {
    delete params_;
  }
  uint32 GetParamCount() const OVERRITE {
    return boost::mpl::size<boost::mpl::vector<VarsType...> >::value;
  }
  const std::stack<Variant> &GetParams() const OVERRITE {
    return *params_;
  }
private:
  std::stack<Variant>* params_;
};

struct FixedStackParamHolder : public StackParamHolder {
  FixedStackParamHolder(const std::stack<Variant> &params) 
    : params_(new std::stack<Variant>(params)) {}
  ~FixedStackParamHolder() {
    delete params_;
  }
  uint32 GetParamCount() const OVERRITE {
    return (uint32)params_->size();
  }
  const std::stack<Variant> &GetParams() const OVERRITE {
    return *params_;
  }
private:
  std::stack<Variant>* params_;
};

TB_NAMESPACE_END

#endif // TB_REFLECTION_PARAM_HOLDER_HPP__
